/*
   Copyright 2011-2012 gtalent2@gmail.com

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
package world

import (
	"../models"
	"time"
)

type MovableSprite struct {
	GenericSprite
	tile       *Tile
	zone       *Zone
	moving     bool
	speed      int
	move       chan byte //1 = up, 2 = down, 3 = left, 4 = right
	moveReturn chan bool
}

func loadMovableSprite(ls *models.Sprite, ms *MovableSprite) {
	ms.GenericSprite = *loadGenericSprite(ls)
	ms.speed = ls.Speed
	ms.move = make(chan byte)
	ms.moveReturn = make(chan bool)
	go func() {
		for {
			dir := <-ms.move
			switch dir {
			case 1:
				ms.moveReturn <- ms.moveUp()
			case 2:
				ms.moveReturn <- ms.moveDown()
			case 3:
				ms.moveReturn <- ms.moveLeft()
			case 4:
				ms.moveReturn <- ms.moveRight()
			default:
				return
			}
		}
	}()
}

func (me *MovableSprite) MoveUp() bool {
	me.move <- 1
	return <-me.moveReturn
}

func (me *MovableSprite) MoveDown() bool {
	me.move <- 2
	return <-me.moveReturn
}

func (me *MovableSprite) MoveLeft() bool {
	me.move <- 3
	return <-me.moveReturn
}

func (me *MovableSprite) MoveRight() bool {
	me.move <- 4
	return <-me.moveReturn
}

func (me *MovableSprite) isMoving() bool {
	return me.moving
}

func (me *MovableSprite) world() *World {
	return me.zone.world
}

func (me *MovableSprite) moveUp() bool {
	//don't even try to lock if not moving, new movement 
	//instructions should not be accepted while in motion
	if me.moving {
		return false
	}
	if !me.moving { //check again now that the lock is in place
		me.moving = true
		tileHeight := tileSize.Height
		goal := me.location.Y - tileHeight
		if goal < 0 {
			me.moving = false
			return false
		}
		newTile := me.zone.tileAt(me.location.X, goal, me.Layer())
		if !newTile.claim(me.tile.occupant, me.zone) {
			me.moving = false
			return false
		}
		oldTile := me.tile
		me.tile = newTile
		for me.location.Y > goal {
			me.zone.world.pause.wait()
			if me.zone.running {
				me.location.Y -= me.speed
				time.Sleep(toNano(15))
			} else {
				me.moving = false
				return false
			}
		}
		me.location.Y = goal
		oldTile.unclaim()
		me.moving = false
		return true
	}
	return false
}

func (me *MovableSprite) moveDown() bool {
	//don't even try to lock if not moving, new movement 
	//instructions should not be accepted while in motion
	if me.moving {
		return false
	}
	if !me.moving { //check again now that the lock is in place
		me.moving = true
		tileHeight := tileSize.Height
		goal := me.location.Y + tileHeight
		if goal >= me.zone.GetBounds().Height {
			me.moving = false
			return false
		}
		newTile := me.zone.tileAt(me.location.X, goal, me.Layer())
		if !newTile.claim(me.tile.occupant, me.zone) {
			me.moving = false
			return false
		}
		oldTile := me.tile
		me.tile = newTile
		for me.location.Y < goal {
			me.zone.world.pause.wait()
			if me.zone.running {
				me.location.Y += me.speed
				time.Sleep(toNano(15))
			} else {
				me.moving = false
				return false
			}
		}
		me.location.Y = goal
		oldTile.unclaim()
		me.moving = false
		return true
	}
	return false
}

func (me *MovableSprite) moveRight() bool {
	//don't even try to lock if not moving, new movement 
	//instructions should not be accepted while in motion
	if me.moving {
		return false
	}
	if !me.moving { //check again now that the lock is in place
		me.moving = true
		tileWidth := tileSize.Width
		goal := me.location.X + tileWidth
		if goal >= me.zone.GetBounds().Width {
			me.moving = false
			return false
		}
		newTile := me.zone.tileAt(goal, me.location.Y, me.Layer())
		if !newTile.claim(me.tile.occupant, me.zone) {
			me.moving = false
			return false
		}
		oldTile := me.tile
		me.tile = newTile
		for me.location.X < goal {
			me.zone.world.pause.wait()
			if me.zone.running {
				me.location.X += me.speed
				time.Sleep(toNano(15))
			} else {
				me.moving = false
				return false
			}
		}
		me.location.X = goal
		oldTile.unclaim()
		me.moving = false
		return true
	}
	return false
}

func (me *MovableSprite) moveLeft() bool {
	//don't even try to lock if not moving, it new movement 
	//instructions should not be accepted while in motion
	if me.moving {
		return false
	}
	if !me.moving { //check again now that the lock is in place
		me.moving = true
		tileWidth := tileSize.Width
		goal := me.location.X - tileWidth
		if goal < 0 {
			me.moving = false
			return false
		}
		newTile := me.zone.tileAt(goal, me.location.Y, me.Layer())
		if !newTile.claim(me.tile.occupant, me.zone) {
			me.moving = false
			return false
		}
		oldTile := me.tile
		me.tile = newTile
		for me.location.X > goal {
			me.zone.world.pause.wait()
			if me.zone.running {
				me.location.X -= me.speed
				time.Sleep(toNano(15))
			} else {
				me.moving = false
				return false
			}
		}
		me.location.X = goal
		oldTile.unclaim()
		me.moving = false
		return true
	}
	return false
}

func (me *MovableSprite) Free() {
	me.GenericSprite.Free()
	close(me.move)
}
